import Geo from './Geo.js';
import Mat from './Mat.js';
import { Matrix4 } from '../three.module.js';

const defAttr = () => ({
    mat: null,
    geo: null,
})

export default class Obj3D {
    constructor(attr) {
        Object.assign(this, defAttr(), attr);

        this.parent = null;
        this.children = [];
    }

    init(gl) {
        const { mat, geo } = this;
        mat.init(gl);
        geo.init(gl, mat.program);
    }

    setUniform(name, uniform) {
        const { mat } = this;
        mat.setData(name, uniform);
    }

    setAttrib(name, attrib) {
        const { geo } = this;
        geo.setData(name, attrib);
    }

    update(gl) {
        const { mat, geo } = this;
        gl.useProgram(mat.program);
        mat.update(gl);
        geo.update(gl);
    }

    draw(gl) {
        const { mode } = this.mat;
        const { drawType, count } = this.geo;
        //gl.useProgram(this.mat.program);//修改的时候已经启动过了
        for (let type of mode) {
            this[`_${drawType}`](gl, count, type);
        }
    }

    _drawArrays(gl, count, mode) {
        gl.drawArrays(gl[mode], 0, count);
    }

    _drawElements(gl, count, mode) {
        gl.drawElements(gl[mode], count, gl.UNSIGNED_BYTE, 0)
    }

    setProgram(program) {
        this.mat.program = program;
    }

    clone() {
        return new Obj3D({
            mat: this.mat.clone(),
            geo: this.geo.clone(),
        });
    }

    default(data) {
        const curData = {};
        Object.assign(curData, obj3DAttr(), data);
        this.geo = new Geo({
            data: {
                'a_Position': {
                    size: curData.size, array: new Float32Array(curData.array)
                },
            },
        });
        if (curData.element) {
            this.geo.setElement(new Uint8Array(curData.element));
        }
        this.mat = new Mat({
            program: curData.program,
            mode: curData.mode,
            data: {
                'u_PvM4': { type: 'uniformMatrix4fv', value: curData.pvM4.elements },
                'u_ModelM4': { type: 'uniformMatrix4fv', value: curData.modelM4.elements },
                'u_PointSize': { type: 'uniform1f', value: curData.PointSize },
                'u_Color': { type: 'uniform4fv', value: curData.color, cRend: true },
            },
        });
        return this;
    }

    createAxis(program) {
        return new Obj3D({
            geo: new Geo({
                data: {
                    'a_Position': {
                        size: 3, array: new Float32Array([
                            10, 0, 0,
                            -10, 0, 0,
                            0, 10, 0,
                            0, -10, 0,
                            0, 0, 10,
                            0, 0, -10,
                        ])
                    },
                },
                element: {
                    array: new Uint8Array([0, 1, 2, 3, 4, 5])
                }
            }),
            mat: new Mat({
                program: program,
                mode: ['LINES'],
                data: {
                    'u_PvM4': { type: 'uniformMatrix4fv', value: new Matrix4().elements },
                    'u_ModelM4': { type: 'uniformMatrix4fv', value: new Matrix4().elements },
                    'u_Color': { type: 'uniform4fv', value: [1, 1, 1, 1], cRend: true },
                }
            })
        });
    }


}

const obj3DAttr = () => ({
    program: null,
    array: [],
    element: null,
    pvM4: new Matrix4(),
    modelM4: new Matrix4(),
    color: [1, 1, 1, 1],
    PointSize: 30,
    size: 3,
    mode: ["POINTS"]
})